//
//  WifiTransferor.m
//  smart_transferor
//
//  Created by  on 11-11-2.
//  Copyright (c) 2011年 tencent. All rights reserved.
//

#import "WifiTransferor.h"

#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>

typedef struct
{
    char opCode;// 1=send,2 = recv
    char fileType;
    unsigned char fileNameLen;
    int sendid;
    int fileLen;
    char filePath[256];
} BroadcastData;

int kUpdListenPort = 20000;
int kTcpListenPort = 30000;

static void ServerTcpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
static void SendTcpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
static void RecvTcpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);

@implementation WifiTransferor

-(char *)getLocalIp
{
    char buf[256];
    if (gethostname(buf, sizeof(buf)))
        return NULL;
    struct hostent *he = gethostbyname(buf);
    if(!he)
    {
        return NULL;
    }
    for (int i=0; he->h_addr_list[i];++i)
    {
        char *ip = inet_ntoa(*(struct in_addr*)he->h_addr_list[i]);
        if (ip != (char *)-1)
        {
            if (strcmp(ip,"127.0.0.1")!=0)
            {
                return ip;
            }
        }
    } 
    return NULL;
}

- (void) reachabilityChanged: (NSNotification* )note
{
    NSLog(@"network status changed");
	Reachability* curReach = [note object];
	NetworkStatus netStatus = [curReach currentReachabilityStatus];
    if (netStatus != ReachableViaWiFi)
    {
        NSLog(@"wifi is not reachable");
        return ;
    }
    else
    {
        NSLog(@"wifi is working well now");
    }
    
    
}

-(id)init 
{
    self = [super init];
    if (self) 
    {
        m_pSendTasks = [[NSMutableArray alloc]init];
        m_pRecvTasks = [[NSMutableArray alloc]init];
        mCallback = nil;
        mSendId = 0;
        mRecvId = 0;
        mTcpServerSock  = nil;
        mUdpServerSock = nil;
        NSString *bound = [[NSBundle mainBundle] bundlePath];
        //m_pPrefixPath = @"Users/issac/Desktop";
        m_pPrefixPath = [bound stringByAppendingString:@"/../Documents"];
        [m_pPrefixPath retain];
        
        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil];
        // check wifi status
        m_pWifiReach = [[Reachability reachabilityForLocalWiFi] retain];
        [m_pWifiReach startNotifier];
        NetworkStatus netStatus = [m_pWifiReach currentReachabilityStatus];
        if (netStatus != ReachableViaWiFi)
        {
            NSLog(@"wifi is not working");
            return self;
        }
    }
    return self;
}

-(void)dealloc
{
    [m_pSendTasks release];
    [m_pRecvTasks release];
    [mCallback release];
    [m_pWifiReach release];
    if (mUdpServerSock != nil && mTcpServerSock != nil)
    {
        CFRelease(mUdpServerSock);
        CFRelease(mTcpServerSock);
    }
    [super dealloc];
}
-(void)setCallback:(id<TransferorCallback>)newcallback
{
    id<TransferorCallback> oldid = mCallback;
    if (oldid)
    {
        [oldid release];
    }
    mCallback = newcallback;
    [newcallback retain];
}


-(void) _sendBroadCast:(SendTask *)task
{
    int                     sock;
    ssize_t                 bytesWritten;
   
    
    FileMetaInfo *file = [task fileInfo];
    int port = kUpdListenPort;
    //sock = CFSocketGetNative(self->mUdpServerSock);
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    int on = 1;
    setsockopt(sock, SOL_SOCKET, SO_BROADCAST,&on,sizeof(on));
    
    BroadcastData data;
    memset((void *)&data, 0, sizeof(data));
    data.opCode = 1;// send request
    data.sendid = [task sendId];
    data.fileLen = [file length];
    data.fileNameLen = [[file filePath] lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]];
    data.fileType = [file type];
    
    NSString *filePath = [file filePath];
    int length = data.fileNameLen;
    [filePath getBytes:data.filePath 
              maxLength:256-1 
              usedLength:NULL 
              encoding:[NSString defaultCStringEncoding] 
              options:0 
              range:NSMakeRange(0, length) 
              remainingRange:NULL];
    NSLog(@"broadcast file:%s",data.filePath);
    
//    
    int sendLen = sizeof(data) -256 + length + 1;
    
    // send broadcast over all the netcark
    
    char buf[256];
    if (gethostname(buf, sizeof(buf)))
    {
        NSLog(@"can't get host name");
        return;
    }
    struct hostent *he = gethostbyname(buf);
    if(!he)
    {
        NSLog(@"get hostname failed");
        return ;
    }
    for (int i=0; he->h_addr_list[i];++i)
    {
        char *ip = inet_ntoa(*(struct in_addr*)he->h_addr_list[i]);
        if (ip != (char *)-1)
        {
            if (strcmp(ip,"127.0.0.1")!=0)
            {
                struct in_addr aaa;
                inet_aton(ip,&aaa);
                ((unsigned char *)&aaa.s_addr)[3] = 255;
                
                char *broadcastAddr = inet_ntoa(aaa); 
                NSLog(@"broadcast address:%s  ,iterately",broadcastAddr);
               
                struct sockaddr_in      addr;
                memset(&addr, 0, sizeof(addr));
                addr.sin_len         = sizeof(addr);
                addr.sin_family      = AF_INET;
                addr.sin_port        = htons(port);
                addr.sin_addr = aaa;
                unsigned char *tmp = (unsigned char *)&addr.sin_addr.s_addr;
                for (int loop = 1; loop < 255; ++loop)
                {
                    tmp[3] = loop;
                    bytesWritten = sendto(sock, (void *)&data, sendLen,0, (const struct sockaddr *)&addr, sizeof(addr));
                }
            }
        }
    } 
    
}

-(int)send:(FileMetaInfo *)file
{   
    NSLog(@"the user request sending file:%@",[file filePath]);
    
    NetworkStatus netStatus = [m_pWifiReach currentReachabilityStatus];
    if (netStatus != ReachableViaWiFi)
    {
        NSLog(@"wifi is still not reachable");
        return -1;
    }
    
    SendTask *task = [[SendTask alloc]init];
    [task setSendId:self->mSendId++];
    [task setFileInfo:file];
    [task setStartTimeStamp:0];
    [self->m_pSendTasks addObject:task];
    [self _sendBroadCast:task];
    [task release];
    return 0;
}

-(SendTask *) getSendTaskById:(int)sendId
{
    NSEnumerator *iter = [self->m_pSendTasks objectEnumerator];
    id obj;
    while (obj = [iter nextObject])
    {
        SendTask *task = (SendTask *)obj;
        if (task && [task sendId] == sendId)
        {
            return task;
        }
    }
    return nil;
}

-(RecvTask *) getRecvTaskById:(int)recvId
{
    NSEnumerator *iter = [self->m_pRecvTasks objectEnumerator];
    id obj;
    while (obj = [iter nextObject])
    {
        RecvTask *task = (RecvTask *)obj;
        if (task && [task recvId] == recvId)
        {
            return task;
        }
    }
    return nil;
}

-(void)_sendTask:(SendTask *)task to:(struct in_addr)addr
{
    if (task)
    {
        int                     err;
        int                     sock;
        const CFSocketContext   context = { 0, self, NULL, NULL, NULL };
        CFRunLoopSourceRef      rls;
        err = 0;
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0) 
        {
            err = errno;
        }
        
        if (err == 0) 
        {
            int flags;
            flags = fcntl(sock, F_GETFL);
            err = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
            if (err < 0) 
            {
                err = errno;
            }
        }
        
        if (err == 0) {
            struct sockaddr_in      address;
            memset(&address, 0, sizeof(address));
            address.sin_len         = sizeof(address);
            address.sin_family      = AF_INET;
            address.sin_port        = htons(kTcpListenPort);
            address.sin_addr = addr;
            err = connect(sock, (const struct sockaddr *)&address, sizeof(address));
            if (err < 0 && errno == EINPROGRESS) 
            {
                err = 0;
            }
        }
        
        if (err == 0) 
        {
            
            CFSocketRef tcpSock = CFSocketCreateWithNative(NULL, sock, kCFSocketWriteCallBack, SendTcpSocketCallback, &context);
            CFOptionFlags sockopt = CFSocketGetSocketFlags (tcpSock );
            /* Set the read callback to be automatically reenabled. */
            sockopt |= kCFSocketAutomaticallyReenableWriteCallBack;
            /* Clear the close-on-invalidate flag. */
            sockopt &= ~kCFSocketCloseOnInvalidate;
            CFSocketSetSocketFlags(tcpSock, sockopt);
            
            sock = -1;
            rls = CFSocketCreateRunLoopSource(NULL, tcpSock, 0);
            CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
            CFRelease(rls);
            // add to sendtask's sendingconn
            SendingConn *sendConn = [[SendingConn alloc] init];
            [sendConn setSendTask:task];
            char *fromIp = inet_ntoa(addr);
            int size = (int )strlen(fromIp) + 1;
            char *dstIp = (char *)malloc(size);
            memset(dstIp, 0, size);
            memcpy(dstIp,fromIp,size);
            [sendConn setDstIp:dstIp];
            [sendConn set_cfSocket:tcpSock];
            [sendConn setTouchTimeStamp:0];//@todo
            [[task sendingConns] addObject:sendConn];
            [sendConn release]; // hold by task
            CFRelease(tcpSock);
        }
        if (sock != -1) 
        {
            close(sock);
        }
    }
}

-(void)_sendReplyTo:(char *)dstIp Request:(int)sendId
{
    int                     sock;
    ssize_t                 bytesWritten;
    //char *localIp = [self getLocalIp];
    int port = kUpdListenPort;
    //sock = CFSocketGetNative(self->mUdpServerSock);
    sock = socket(AF_INET, SOCK_DGRAM, 0);       


    
    struct sockaddr_in      addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_len         = sizeof(addr);
    addr.sin_family      = AF_INET;
    addr.sin_port        = htons(port);
    addr.sin_addr.s_addr = inet_addr(dstIp);
    
    char buf[5];
    memset(buf,0,sizeof(buf));
    buf[0] = 2; //accept
    int *sendid = (int *)(buf+1);
    *sendid = sendId;
    bytesWritten = sendto(sock, (void *)buf, 5,0, (const struct sockaddr *)&addr, sizeof(addr));
    if (bytesWritten == 5)
    {
        NSLog(@"send reply successfully!");
    }
    else
    {
        NSLog(@"send reply failed!");
    }

}
-(void)actionForRecv:(int)recvId accept:(bool)isAccept
{
    if(isAccept)
    {
        RecvTask *task = [self getRecvTaskById:recvId];
        if (task == nil)
        {
            NSLog(@"can't find the recvtask for recvId:%d",recvId);
            return;
        }
        char *toIp = [task srcIp];
        int srcSendId = [task srcSendId];
        [self _sendReplyTo:toIp Request:srcSendId];
    }
    else
    {
        return;
    }
}

-(bool)isLocalIp:(struct in_addr)ip4
{
    char buf[256];
    if (gethostname(buf, sizeof(buf)))
        return NULL;
    struct hostent *he = gethostbyname(buf);
    if(!he)
    {
        NSLog(@"get hostname failed");
        return NULL;
    }
    for (int i=0; he->h_addr_list[i];++i)
    {
        struct in_addr local = *((struct in_addr*)he->h_addr_list[i]);
        if ( local.s_addr == ip4.s_addr)
        {
            return true;
        }
    }
    return false;
}

-(NSString *)getSavePath:(NSString *)filePath
{
    if (filePath == nil)
    {
        return nil;
    }
    NSArray *listItems = [filePath componentsSeparatedByString:@"/"];
    NSString *lastItem = [listItems lastObject];
    NSString *adjustFilePath = nil;
    adjustFilePath = [[self->m_pPrefixPath stringByAppendingString:@"/"] stringByAppendingString:[listItems lastObject]];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:adjustFilePath])
    {
        return [[adjustFilePath retain]autorelease];
    }
    
    int len = [lastItem length];
    int index = len -1;
    // check if it has extension name,find the '.'
    while(index >= 0)
    {
        unichar c = [lastItem characterAtIndex:index];
        if ( c == '.')
        {
            break;
        }
        else
        {
            --index;
        }
    }
    int fileNameLenWithoutSuffix = len;
    NSString *extensionName = @"";
    if (index >= 0)
    {
        fileNameLenWithoutSuffix = index;
        extensionName = [lastItem substringFromIndex:index + 1];
        index = index - 1;
    }
 
    index = fileNameLenWithoutSuffix - 1;
    while(index >= 0)
    {
        unichar c = [lastItem characterAtIndex:index];
        if ( c <= '9' && c >= '0')
        {
            --index;
        }
        else
        {
            break;
        }
    }
    if (index < 0)
    {
        index = fileNameLenWithoutSuffix -1;
    }
    else
    {
        if ([lastItem characterAtIndex:index] == '_')
        {
            --index;
        }
        else
        {
            index = fileNameLenWithoutSuffix -1;
        }
    }
    NSString *fileName = [lastItem substringToIndex:index + 1];
    NSString *prefix = [[self->m_pPrefixPath stringByAppendingString:@"/"] stringByAppendingString:fileName];
    index = 1;
    while(1)
    {
        adjustFilePath = [prefix stringByAppendingFormat:@"_%d.%@",index,extensionName];
        if ([fileManager fileExistsAtPath:adjustFilePath])
        {
            ++index;
        }
        else
        {
            break;
        }
    }
    return adjustFilePath;
 }

-(void)_udpCallback:(CFSocketRef)s :(CFSocketCallBackType)type :(CFDataRef) address :(const void *)data :(void *) info
{
//    if (!self->mCallback)
//    {
//        return ;
//    }
    NSLog(@"upd recved some data");
    if ( s == self->mUdpServerSock)
    {
        if (type == kCFSocketDataCallBack)
        {
            struct sockaddr_in remoteAddress;
            CFDataGetBytes(address, CFRangeMake(0, CFDataGetLength(address)), (UInt8 *)&remoteAddress);
            if ([self isLocalIp:remoteAddress.sin_addr])
            {
                return;
            }
            int bufLen = (int)CFDataGetLength(data);
            UInt8 *buf = (UInt8 *)malloc(bufLen);
            memset(buf,0,bufLen);
            CFDataGetBytes(data, CFRangeMake(0,bufLen), buf);
            
            if (buf[0] == 1) // recv send request
            {
                BroadcastData *castData = (BroadcastData *) buf;  // @todo:free this
                
                FileMetaInfo *fileMetaInfo = [[FileMetaInfo alloc] init];
                
                NSString *filePath = [NSString stringWithCString:castData->filePath encoding:[NSString defaultCStringEncoding]];
                NSString *adjustFilePath = [self getSavePath:filePath];
                NSLog(@"recved a request to send file:%@",filePath);
                NSLog(@"saving to:%@",adjustFilePath);
                
                // new FileMetaInfo
                [fileMetaInfo setType:castData->fileType];
                [fileMetaInfo setLength:castData->fileLen];
                [fileMetaInfo setFilePath:adjustFilePath];
                int recvId = self->mRecvId++;
                
                // add to taskList
                
                int srcSendId = castData->sendid;
                int startTimeStamp = 0;
                
                RecvTask * recvTask = [[RecvTask alloc] init];
                char *fromIp = inet_ntoa(remoteAddress.sin_addr);
                int ipLen = (int)strlen(fromIp);
                char *ipBuf = (char *)malloc(ipLen + 1);
                memset(ipBuf,0,ipLen+1);
                memcpy(ipBuf,fromIp,ipLen);
                [recvTask setSrcIp:ipBuf];
                [recvTask setSrcSendId:srcSendId];
                [recvTask setRecvId:recvId];
                [recvTask setStartTimeStamp:startTimeStamp];
                [recvTask setFileInfo:fileMetaInfo];
                [fileMetaInfo release];
                [self->m_pRecvTasks addObject:recvTask];
                [recvTask release];
                if (mCallback)  
                {
                    NSLog(@"new filecoming,do callback,recvId:%d",recvId);
                    [mCallback newFileComing:fileMetaInfo identity:recvId];
                }
                free(buf);
                //do better test
                //[self actionForRecv:[recvTask recvId] accept:true];
            }
            else if(buf[0] == 2) // recv reply
            {
                int *requestSendId = (int *)(buf + 1);
                SendTask *task = [self getSendTaskById:*requestSendId];
                if (task)
                {
                    // @todo: check the timestamp
                    // send the task
                    [self _sendTask:task to:remoteAddress.sin_addr];
                }
                free(buf);
            }
        }
    }
}

static void UdpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
    WifiTransferor *obj;
    obj = (WifiTransferor *)info;
    [obj _udpCallback:s :type :address :data :info];
}

-(BOOL)_startUdpServer
{
    int                     err;
    int                     sock;
    const CFSocketContext   context = { 0, self, NULL, NULL, NULL };
    CFRunLoopSourceRef      rls;
    err = 0;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    
    if (sock < 0) 
    {
        err = errno;
    }
    
    if (err == 0) {
        struct sockaddr_in      addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_len         = sizeof(addr);
        addr.sin_family      = AF_INET;
        addr.sin_port        = htons(kUpdListenPort);
        addr.sin_addr.s_addr = INADDR_ANY;
        err = bind(sock, (const struct sockaddr *) &addr, sizeof(addr));
        int on =1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,&on,sizeof(on));// what does on mean?
        if (err < 0) 
        {
            err = errno;
        }
    }
    if (err == 0) 
    {
        int flags;
        flags = fcntl(sock, F_GETFL);
        err = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
        if (err < 0) 
        {
            err = errno;
        }
    }
    if (err == 0) 
    {
        self->mUdpServerSock = CFSocketCreateWithNative(NULL, sock, kCFSocketDataCallBack, UdpSocketCallback, &context);
        sock = -1;
        rls = CFSocketCreateRunLoopSource(NULL, self->mUdpServerSock, 0);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
        CFRelease(rls);
        NSLog(@"udpserver listen success");
    }
    else
    {
        NSLog(@"udpserver listen failed");
    }
    if (sock != -1) 
    {
        close(sock);
    }
    return (err==0);
}

-(void)_serverTcpCallback:(CFSocketRef)s :(CFSocketCallBackType)type :(CFDataRef) address :(const void *)data :(void *) info
{
    if (s == self->mTcpServerSock) // accept new recving conn
    {
        if (type==kCFSocketAcceptCallBack )
        {
            int                     err;
            int                     sock;
            const CFSocketContext   context = { 0, self, NULL, NULL, NULL };
            CFRunLoopSourceRef      rls;
            err = 0;
            sock = *((int *)data); // new fd
            if (err == 0) 
            {
                int flags;
                flags = fcntl(sock, F_GETFL);
                err = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
                if (err < 0) 
                {
                    err = errno;
                }
            }
                      
            if (err == 0) 
            {
                // add to runloop
                CFSocketRef tcpSock = CFSocketCreateWithNative(NULL, sock, kCFSocketDataCallBack, RecvTcpSocketCallback, &context);
                sock = -1;
                rls = CFSocketCreateRunLoopSource(NULL, tcpSock, 0);
                CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
                CFRelease(rls);
                CFRelease(tcpSock);
                NSLog(@"tcp server accept a recv connection");
            }
            if (sock != -1) 
            {
                close(sock);
            }
            
        }
    }
}

-(SendingConn *)getSendingConnBySocketRef:(CFSocketRef)s
{
    NSEnumerator *iter = [self->m_pSendTasks objectEnumerator];
    id obj;
    while (obj = [iter nextObject])
    {
        SendTask *task = (SendTask *)obj;
        NSEnumerator *iter_conn = [[task sendingConns] objectEnumerator];
        id obj_conn;
        while (obj_conn = [iter_conn nextObject]) 
        {
            SendingConn *conn = (SendingConn *)obj_conn;
            if ([conn _cfSocket] == s)
            {
                return conn;
            }
        }
    }
    return nil;
}



-(void)_sendTcpCallback:(CFSocketRef)s :(CFSocketCallBackType)type :(CFDataRef) address :(const void *)data :(void *) info
{
    NSLog(@"in in in send");
    SendingConn *conn = [self getSendingConnBySocketRef:s];
    if (conn==nil)
    {
        return ;
    }
    if (type == kCFSocketConnectCallBack)
    {
        // @todo,maybe do nothing
    }
    else if (type == kCFSocketWriteCallBack)
    {
        // send sendid + filelen + filedata
        int sockfd = CFSocketGetNative(s);
        SendTask *task = [conn sendTask];
        if ([conn infd] == -1) // the first sending time
        {
            NSString *path = [[task fileInfo] filePath];
            char filePathBuf[256];
            memset(filePathBuf,0,sizeof(filePathBuf));
            int length = (int)[path lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]];
            [path getBytes:filePathBuf
                     maxLength:256-1 
                    usedLength:NULL 
                      encoding:[NSString defaultCStringEncoding] 
                       options:0 
                         range:NSMakeRange(0, length) 
                remainingRange:NULL];
            // send sendid
            int sendId = [task sendId];
            int infd = open(filePathBuf, O_RDONLY);
            if (infd == -1)
            {
                //@todo clean the conn
                NSLog(@"file:%s can't not be opened",filePathBuf);
                [self->m_pSendTasks removeObject:task];
                close(sockfd);
                return ;
            }
            [conn setInfd:infd];
            write(sockfd, (void *)&sendId, 4);
        }
        // send data until kernel buffer full
        while (1)
        {
            // send data
            char buf[1024];
            memset(buf,0,sizeof(buf));
            int infd = [conn infd];
            int readSize = (int)read(infd,buf,1024);
            if (readSize == 0)
            {
                // @todo:send over,do some cleaning,success,send callback
                close(infd);
                close(sockfd);
                NSLog(@"send file finished to:%s",[conn dstIp]);
                [[task sendingConns]removeObject:conn];
                return ;
            }
            else if (readSize == -1)
            {
                // @todo: read error,clean the conn,failed
                close(infd);
                close(sockfd);
                NSLog(@"send file failed to:%s 111 error:%d",[conn dstIp],errno);
                [[task sendingConns]removeObject:conn];
                return ;
            }
            int sendSize = (int)write(sockfd, buf, readSize);
            
            if (sendSize == -1)
            {
                // write error,clean the conn,failed
                if (errno == EAGAIN)
                {
                    return ;
                }
                close(infd);
                close(sockfd);
                NSLog(@"send file failed to:%s  222 error:%d",[conn dstIp],errno);
                [[task sendingConns]removeObject:conn];
                return ;
            }
            else if(sendSize < readSize) // buffer is full
            {
                // rollback the file
                lseek(infd, readSize - sendSize, SEEK_CUR);
                break;//here the kernel buffer is full
            }
            int sentBytes = [conn sentBytes];
            sentBytes += sendSize;
            [conn setSentBytes:sentBytes];
            NSLog(@"sent to dst:%s bytes:%d",[conn dstIp],[conn sentBytes]);
            break;
        }
    }
    
}

-(RecvingConn *)getRecvingConnBySocketRef:(CFSocketRef)s
{
    NSEnumerator *iter = [self->m_pRecvTasks objectEnumerator];
    id obj;
    while (obj = [iter nextObject])
    {
        RecvTask *task = (RecvTask *)obj;
        RecvingConn *conn = [task recvingConn];
        if (conn && [conn _cfSocket] ==s)
        {
            return conn;
        }
    }
    return nil;
}

-(RecvTask *)getRecvTaskBySrcSendId:(int)sendId andSrcIp:(char *)srcIp
{
    NSEnumerator *iter = [self->m_pRecvTasks objectEnumerator];
    id obj;
    while (obj = [iter nextObject])
    {
        RecvTask *task = (RecvTask *)obj;
        if (task && [task srcSendId] == sendId)
        {
            if (strcmp(srcIp,[task srcIp])==0)
            {
                return task;
            }
        }
    }
    return nil;
}

-(void)_recvTcpCallback:(CFSocketRef)s :(CFSocketCallBackType)type :(CFDataRef) address :(const void *)data :(void *) info
{
    if (type == kCFSocketDataCallBack)
    {
        struct sockaddr_in remoteAddress;
        CFDataGetBytes(address, CFRangeMake(0, CFDataGetLength(address)), (UInt8 *)&remoteAddress);
        char *fromIp = inet_ntoa(remoteAddress.sin_addr);
        
        int bufLen = (int)CFDataGetLength(data);
        UInt8 *buf = (UInt8 *)malloc(bufLen);
        memset(buf,0,bufLen);
        CFDataGetBytes(data, CFRangeMake(0,bufLen), buf);
        int len = (int )CFDataGetLength(data);
        NSLog(@"data recv from:%s size:%d",fromIp,len);
        if (len == 0)
        {
            return ;
        }
        RecvingConn *conn = [self getRecvingConnBySocketRef:s];
        RecvTask *task=nil;
        if (conn == nil)
        {
            // the first time to read
            if (len < 4)
            {
                // @todo:bad thing
                NSLog(@"very bad thing");
            }
            if (len >= 4)
            {
                int sendid = (*(int *)buf);
                task = [self getRecvTaskBySrcSendId:sendid andSrcIp:fromIp]; // both conditions
                if (task)
                {
                    // new recvconn
                    conn = [[RecvingConn alloc]init];
                    [task setRecvingConn:conn];
                    [conn setRecvTask:task];
                    [conn set_cfSocket:s];
                    NSLog(@"hhhh");
                    // @todo:write file
                    NSString *filePath = [[task fileInfo]filePath];
                    char fileName[256];
                    memset(fileName,0,sizeof(fileName));
                    int length = (int)[filePath lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]];
                    [filePath getBytes:fileName 
                             maxLength:256-1 
                            usedLength:NULL 
                              encoding:[NSString defaultCStringEncoding] 
                               options:0 
                                 range:NSMakeRange(0, length) 
                        remainingRange:NULL];
                    NSLog(@"filePath:%s",fileName);
                    int outfd = open(fileName, O_CREAT | O_RDWR);
                    fchmod(outfd,0666);
                    if (outfd <=0 )
                    {
                        NSLog(@"open file for write error");
                        [self->m_pRecvTasks removeObject:task];
                        int sock = CFSocketGetNative(s);
                        close(sock);
                        [conn release];
                        return ;
                    }
                    [conn setOutfd:outfd];
                    write(outfd,buf + 4,len - 4);
                    int recvLen = [conn recvLength];
                    recvLen = recvLen + len - 4;
                    [conn setRecvLength:recvLen];
                }
                else
                {
                    // @todo:no request before,delete the tcpconn
                    int sock = CFSocketGetNative(s);
                    close(sock);
                    return;
                }
            }
        }
        else
        {
            [conn retain]; // balance
            task = [conn recvTask];
            // @todo:write file
            int outfd = [conn outfd];
            write(outfd, buf, len);
            int recvLen = [conn recvLength];
            recvLen = recvLen + len;
            [conn setRecvLength:recvLen];
        }
        NSLog(@"current:%d,total:%d",[conn recvLength],[[task fileInfo]length]);
        if ([conn recvLength] == [[task fileInfo]length])
        {
            // success recv the file
            // @todo clean and close,send callback
            if(mCallback)
            {
                NSLog(@"recv id:%d finished,do callback",[task recvId]);
                [mCallback didRecvFinished:[task recvId]];
            }
            [self->m_pRecvTasks removeObject:task];
            int sock = CFSocketGetNative(s);
            close(sock);
            NSLog(@"recv file success");
        }
        [conn release];
        free(buf);
    }
    
}

static void ServerTcpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
    WifiTransferor *obj = NULL;
    obj = (WifiTransferor *)info;
    [obj _serverTcpCallback:s :type :address :data :info];
}

static void SendTcpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
    WifiTransferor *obj;
    obj = (WifiTransferor *)info;
    [obj _sendTcpCallback:s :type :address :data :info];
}

static void RecvTcpSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
    WifiTransferor *obj;
    obj = (WifiTransferor *)info;
    [obj _recvTcpCallback:s :type :address :data :info];
}

-(BOOL)_startTcpServer
{
    int                     err;
    int                     sock;
    const CFSocketContext   context = { 0, self, NULL, NULL, NULL };
    CFRunLoopSourceRef      rls;
    err = 0;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) 
    {
        err = errno;
    }
    
    if (err == 0) 
    {
        struct sockaddr_in      addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_len         = sizeof(addr);
        addr.sin_family      = AF_INET;
        addr.sin_port        = htons(kTcpListenPort);
        addr.sin_addr.s_addr = INADDR_ANY;
        err = bind(sock, (const struct sockaddr *) &addr, sizeof(addr));
        if (err < 0) 
        {
            err = errno;
        }
    }
    if (err == 0) 
    {
        int flags;
        flags = fcntl(sock, F_GETFL);
        err = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
        int on =1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,&on,sizeof(on));// what does on mean?
        if (err < 0) 
        {
            err = errno;
        }
    }
    
    if (err == 0)
    {
        err = listen(sock, SOMAXCONN);
        if (err < 0) 
        {
            err = errno;
        }
    }
    
    if (err == 0) 
    {
        self->mTcpServerSock = CFSocketCreateWithNative(NULL, sock, kCFSocketAcceptCallBack, ServerTcpSocketCallback, &context);
        sock = -1;
        rls = CFSocketCreateRunLoopSource(NULL, self->mTcpServerSock, 0);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
        CFRelease(rls);
        NSLog(@"tcpserver listen success");

    }
    else
    {
        NSLog(@"tcpserver listen failed");
    }
    if (sock != -1) 
    {
        close(sock);
    }
    return (err==0);
}

-(int)startListen
{
    return [self _startTcpServer] && [self _startUdpServer];
}

@end
